﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Allegro.MathInterface;
//using Cosmologica.MathInterface;

//using System.Linq;
//using System.Text;

namespace Allegro.MathLib
{
    /// <summary>
    /// Class for serializing a function f to file. The file can be either a text file in ASCII format or a binary file.
    /// Different file extensions should be used to distinguish between ASCII- and binary-formatted files.
    /// ASCII files are formatted as follows:
    /// Each line holds a pair of (x, y) values, where y = f(x).
    /// Binary files are formatted as follows:
    /// Header Rec*
    /// Header is a 4 byte signed int (Int32) holding the number of records. Each record 16 byte and consists of pair of doubles x y,
    /// where y = f(x)
    /// </summary>
    public class FunctionSerializer
    {
        /// <summary>
        /// Serialize f to an ASCII file using a TextWriter. Give the start and end values of x as
        /// x0 and x1, respectively. A sample interval dx must be given as well
        /// </summary>
        /// <param name="f"></param>
        /// <param name="writer"></param>
        /// <param name="x0"></param>
        /// <param name="x1"></param>
        /// <param name="dx"></param>
        static public void Serialize(Func<double, double> f, TextWriter writer, double x0, double x1, double dx)
        {
            double x = x0;
            double nSamples = (int)((x1 - x0) / dx) + 1;

            Serialize(f, writer, x0, x1, nSamples);
        }
        /// <summary>
        /// Serialize f to an Ascii file using a TextWriter.  Give the start and end values of x as
        /// x0 and x1, respectively. The number of samples taken must be given as well
        /// </summary>
        /// <param name="f"></param>
        /// <param name="writer"></param>
        /// <param name="x0"></param>
        /// <param name="x1"></param>
        /// <param name="nSamples"></param>
        static public void Serialize(Func<double, double> f, TextWriter writer, double x0, double x1, int nSamples)
        {
            if (f == null)
                throw new ApplicationException("FunctionSerializer error: No function to serialize");

            double dx = (x1 - x0) / nSamples;
            for (int i = 0; i < nSamples; i++)
            {
                double x = dx * i;
                writer.Write("{0,-23:G}", x.ToString("G", CultureInfo.InvariantCulture));
                writer.Write("{0,-23:G}", f(x).ToString("G", CultureInfo.InvariantCulture));
                writer.Write(Environment.NewLine);
            }
        }
        /// <summary>
        /// Serialize f to a binary file using a BinaryWriter. Give the start and end values of x as
        /// x0 and x1, respectively. A sample interval dx must be given as well
        /// </summary>
        /// <param name="f"></param>
        /// <param name="writer"></param>
        /// <param name="x0"></param>
        /// <param name="x1"></param>
        /// <param name="dx"></param>
        static public void Serialize(Func<double, double> f, BinaryWriter writer, double x0, double x1, double dx)
        {
            double x = x0;
            int nSamples = (int)((x1 - x0) / dx) + 1;
            Serialize(f, writer, x0, x1, nSamples);
        }
        /// <summary>
        /// Serialize f to a binary file using a BinaryWriter. Give the start and end values of x as
        /// x0 and x1, respectively. The number of samples taken must be given as well
        /// </summary>
        /// <param name="f"></param>
        /// <param name="writer"></param>
        /// <param name="x0"></param>
        /// <param name="x1"></param>
        /// <param name="nSamples"></param>
        static public void Serialize(Func<double, double> f, BinaryWriter writer, double x0, double x1, int nSamples)
        {
            if (f == null)
                throw new ApplicationException("FunctionSerializer error: No function to serialize");

            double dx = (x1 - x0) / nSamples;
            writer.Write(nSamples);
            for (int i = 0; i < nSamples; i++)
            {
                double x = dx * i;
                writer.Write(x);
                writer.Write(f(x));
            }
        }
        /// <summary>
        /// Serialie f to a file with a given file path. Give the start and end values of x as
        /// x0 and x1, respectively. The number of samples taken must be given as well. 
        /// If binary = true, the file will be written in binary format, otherwise it will be
        /// written in ASCII form.
        /// </summary>
        /// <param name="f"></param>
        /// <param name="path"></param>
        /// <param name="x0"></param>
        /// <param name="x1"></param>
        /// <param name="nSamples"></param>
        /// <param name="binary"></param>
        public static void Serialize(Func<double, double> f, string path, double x0, double x1, int nSamples, bool binary)
        {
            if (binary)
            {
                BinaryWriter writer = new BinaryWriter(File.Open(path,FileMode.Create));
                Serialize(f, writer, x0, x1, nSamples);
                writer.Close();
            }
            else
            {
                TextWriter writer = new StreamWriter(path);
                Serialize(f, writer, x0, x1, nSamples);
                writer.Close();
            }
        }
        /// <summary>
        /// Serialie f to a file with a given file path. Give the start and end values of x as
        /// x0 and x1, respectively. A sample interval dx must be given as well. 
        /// If binary = true, the file will be written in binary format, otherwise it will be
        /// written in ASCII form.
        /// </summary>
        /// <param name="f"></param>
        /// <param name="path"></param>
        /// <param name="x0"></param>
        /// <param name="x1"></param>
        /// <param name="dx"></param>
        /// <param name="binary"></param>
        public static void Serialize(Func<double,double> f, string path, double x0, double x1, double dx, bool binary)
        {
            if (binary)
            {
                BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create));
                Serialize(f, writer, x0, x1, dx);
                writer.Close();
            }
            else
            {
                TextWriter writer = new StreamWriter(path);
                Serialize(f, writer, x0, x1, dx);
                writer.Close();
            }
        }
        /// <summary>
        /// Deserialize a function from an ASCII file using a TextReader. A Spline of the function
        /// will be returned.
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        static public Spline Deserialize(TextReader reader)
        {
            List<double> xValues = new List<double>();
            List<double> yValues = new List<double>();
            char[] delims = new char[] { ' ' };
            string line = reader.ReadLine();
            while (line != null)
            {
                int blankPos = line.IndexOfAny(delims);
                string xstring = line.Substring(0, blankPos);
                string ystring = line.Substring(blankPos + 1);
                double x = double.Parse(xstring, CultureInfo.InvariantCulture);
                double y = double.Parse(ystring, CultureInfo.InvariantCulture);
                xValues.Add(x);
                yValues.Add(y);
                line = reader.ReadLine();
            }
            Spline f = new Spline(xValues.ToArray(), yValues.ToArray());
            return f;
        }
        /// <summary>
        /// Deserialize a function from a binary file using a BinaryReader. A Spline of the function
        /// will be returned.
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        static public Spline Deserialize(BinaryReader reader)
        {
            int nSamples = reader.ReadInt32();
            double[] xValues = new double[nSamples];
            double[] yValues = new double[nSamples];
            for (int i = 0; i < nSamples; i++)
            {
                double x = reader.ReadDouble();
                double y = reader.ReadDouble();
                xValues[i] = x;
                yValues[i] = y;
            }
            Spline f = new Spline(xValues, yValues);
            return f;
        }
        /// <summary>
        /// Deserialize a function from a file with a given path. If the file is a binary
        /// file, set binary = true. If the file is an ASCII file, set binary = false.
        /// </summary>
        /// <param name="path"></param>
        /// <param name="binary"></param>
        /// <returns></returns>
        static public Spline Deserialize(string path, bool binary)
        {
            Spline f;
            if (binary)
            {
                BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read));
                f = Deserialize(reader);
                reader.Close();
            }
            else
            {
                TextReader reader = new StreamReader(path);
                f = Deserialize(reader);
                reader.Close();
            }
            return f;
        }
    }
}
